package com.test.utils.jira;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.testng.ITestListener;
import org.testng.ITestResult;

import com.microsoft.playwright.Page;
import com.test.BaseTest;
import com.test.utils.ConfigManager;

import io.qameta.allure.Allure;
import io.qameta.allure.Severity;
import io.qameta.allure.SeverityLevel;

public class JiraTestListener implements ITestListener {
    private JiraClient jiraClient;
    private boolean jiraEnabled;
    private String projectKey;

    public JiraTestListener() {
        ConfigManager config = ConfigManager.getInstance();
        this.jiraEnabled = config.getBooleanProperty("jira.enabled", true);

        if (jiraEnabled) {
            String jiraUrl = config.getProperty("jira.url");
            String jiraToken = config.getProperty("jira.personal.token");
            boolean sslVerify = config.getBooleanProperty("jira.ssl.verify", false);
            this.projectKey = config.getProperty("jira.project.key");

            if (jiraToken == null || jiraToken.trim().isEmpty()) {
                System.out.println("JIRA personal token not provided. JIRA integration will be disabled for this run.");
                System.out.println("To enabled JIRA integration, provide token via:");
                System.out.println("  - System property: -Djira.personal.token=<token>");
                System.out.println("  - Environment variable: JIRA_PERSONAL_TOKEN=<token>");
                System.out.println("  - Or in test.properties file");
                this.jiraEnabled = false;
                return;
            }

            if (jiraUrl == null || projectKey == null) {
                System.out.println("JIRA configuration incomplete (missing jiraUrl or projectKey)");
                this.jiraEnabled = false;
                return;
            }

            try {
                this.jiraClient = new JiraClient(jiraUrl, jiraToken, sslVerify);
            } catch (Exception e) {
                this.jiraEnabled = false;
            }
        } else {
            //
        }
    }

    private String mapSeverityToJiraPriority(SeverityLevel severityLevel) {
        if (severityLevel == null) {
            return "High";
        }

        switch (severityLevel) {
            case BLOCKER:
                return "Highest";
            case CRITICAL:
                return "High";
            case NORMAL:
                return "Medium";
            case MINOR:
                return "Low";
            case TRIVIAL:
                return "Lowest";
            default:
                return "High";
        }
    }

    private String mapSeverityToJiraSeverity(SeverityLevel severityLevel) {
        if (severityLevel == null) {
            return "High";
        }

        return switch (severityLevel) {
            case BLOCKER -> "Critical";
            case CRITICAL -> "Highest";
            case NORMAL -> "Medium";
            case MINOR -> "Low";
            case TRIVIAL -> "Low";
            default -> "High";
        };
    }

    private SeverityLevel extractSeverityLevel(ITestResult result) {
        try {
            Method method = result.getMethod().getConstructorOrMethod().getMethod();
            if (method != null && method.isAnnotationPresent(Severity.class)) {
                Severity severity = method.getAnnotation(Severity.class);
                return severity.value();
            }
        } catch (Exception e) {
            System.err.println("Failed to extract severity annotation: " + e.getMessage());
        }
        return null;
    }

    @Override
    public void onTestFailure(ITestResult result) {
        if (!jiraEnabled || jiraClient == null) {
            return;
        }

        try {
            String testClassName = result.getTestClass().getName();
            String testMethodName = result.getMethod().getMethodName();
            String testDescription = result.getMethod().getDescription();

            SeverityLevel severityLevel = extractSeverityLevel(result);
            String jiraPriority = mapSeverityToJiraPriority(severityLevel);
            String jiraSeverity = mapSeverityToJiraSeverity(severityLevel);

            Throwable throwable = result.getThrowable();
            String errorMessage = throwable != null ? throwable.getMessage() : "Unknown error";
            String stackTrace = getStackTrace(throwable);
            String summary = String.format("[Automated Test Failure] %s.%s", testClassName, testMethodName);

            StringBuilder description = new StringBuilder();
            description.append("h3. Test Information\n");
            description.append("* *Test Class:* ").append(testClassName).append("\n");
            description.append("* *Test Method:* ").append(testMethodName).append("\n");
            if (testDescription != null && !testDescription.isEmpty()) {
                description.append("* *Test Description:* ").append(testDescription).append("\n");
            }
            description.append("* *Test Severity:* ").append(severityLevel != null ? severityLevel.name() : "Not specified").append("\n");
            description.append("* *JIRA Priority:* ").append(jiraPriority).append("\n");
            description.append("* *JIRA Severity:* ").append(jiraSeverity).append("\n");
            description.append("* *Execution Time:* ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("\n");
            description.append("* *Duration:* ").append((result.getEndMillis() - result.getStartMillis()) / 1000.0).append(" seconds\n");
            description.append("\n");

            description.append("h3. Error Details\n");
            description.append("* *Error Message:* ").append(errorMessage).append("\n");
            description.append("\n");

            description.append("h3. Stack Trace\n");
            description.append("{code:java}\n");
            description.append(stackTrace);
            description.append("\n");
            description.append("{code}\n");
            description.append("\n");

            description.append("h3. Environment\n");
            ConfigManager config = ConfigManager.getInstance();
            description.append("* *Base URL:* ").append(config.getSaraBaseUrl()).append("\n");
            description.append("* *Browser:* Chromium\n");
            description.append("* *Headless:* ").append(config.isBrowserHeadless()).append("\n");

            List<String> labels = new ArrayList<>();
            labels.add("automated-test");
            labels.add("test-failure");
            labels.add(testClassName.substring(testClassName.lastIndexOf('.') + 1));

            Map<String, Object> customFields = new HashMap<>();

            ConfigManager configManager = ConfigManager.getInstance();

            customFields.put("customfield_11200", Map.of("value", jiraSeverity));
            String impactedEnv = configManager.getProperty("jira.custom.impacted.environment", "Test");
            customFields.put("customfield_19302", Map.of("value", impactedEnv));

            String issueKey = jiraClient.createIssue(
                projectKey,
                "Bug",
                summary,
                description.toString(),
                jiraPriority,
                labels,
                customFields
            );

            attachScreenshotToJira(result, issueKey);

            Allure.link("JIRA Issue", "issue", config.getProperty("jira.url") + "/browse/" + issueKey);

            String comment = String.format(
                "This issue was automatically created by the test automation framework.\n" +
                "Test failed at: %s\n" +
                "Test duration: %.2f seconds",
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                (result.getEndMillis() -result.getStartMillis()) / 1000.0
            );

        } catch (Exception e) {
            String debugMode = System.getProperty("jira.debug", "false");
            if ("true".equalsIgnoreCase(debugMode)) {
                e.printStackTrace();
            }
        }
    }

    private void attachScreenshotToJira(ITestResult result, String issueKey) {
        try {
            Object testInstance = result.getInstance();
            if (testInstance instanceof BaseTest) {
                BaseTest baseTest = (BaseTest) testInstance;
                String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"));
                String screenshotFileName = "failure_" + result.getMethod().getMethodName() + "_" + timestamp + ".png";
                Path screenshotPath = Paths.get("target/screenshots/" + screenshotFileName);

                Files.createDirectories(screenshotPath.getParent());

                try {
                    java.lang.reflect.Field pageField = BaseTest.class.getDeclaredField("page");
                    pageField.setAccessible(true);
                    Page page = (Page) pageField.get(baseTest);

                    if (page != null && !page.isClosed()) {
                        page.screenshot(new Page.ScreenshotOptions()
                            .setPath(screenshotPath)
                            .setFullPage(true));
                        
                        // Attach to JIRA
                        File screenshotFile = screenshotPath.toFile();
                        if (screenshotFile.exists()) {
                            boolean attached = jiraClient.addAttachment(issueKey, screenshotFile);
                            if (attached) {
                                System.out.println("Screenshot attached to JIRA issue: " + issueKey);
                            }
                        }
                    }
                } catch (Exception e) {
                    //
                }
            }
            
            Path screenshotsDir = Paths.get("target/screenshots");
            if (Files.exists(screenshotsDir)) {
                Files.list(screenshotsDir)
                    .filter(path -> path.toString().endsWith(".png"))
                    .sorted((p1, p2) -> {
                        try {
                            return Files.getLastModifiedTime(p2).compareTo(Files.getLastModifiedTime(p1));
                        } catch (Exception e) {
                            return 0;
                        }
                    })
                    .limit(3)
                    .forEach(path -> {
                        try {
                            File file = path.toFile();
                            if (file.exists() && file.length() > 0) {
                                boolean attached = jiraClient.addAttachment(issueKey, file);
                                if (attached) {
                                    System.out.println("Additional screenshot attached: " + file.getName());
                                }
                            }
                        } catch (Exception e) {
                            System.err.println("Failed to attach");
                        }
                    });
            }
        } catch (Exception e) {
            System.err.println("Failed to attach");
            e.printStackTrace();
        }
    }

    private String getStackTrace(Throwable throwable) {
        if (throwable == null) {
            return "No stack trace available";
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        return sw.toString();
    }
}
